// popen.cpp

#include <popen.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/shared_ptr.hpp>

#ifdef _WIN32
# include "win32/process_traits_win32.hpp"
#else // POSIX
# include "posix/process_traits_posix.hpp"
#endif


//####################################################################################################
// popen pimpl
//####################################################################################################

struct process::popen::impl
{
    boost::shared_ptr<process::detail::process_traits_impl> ptr;

	impl(const char *cmd, int flags) : ptr(new detail::process_traits_impl(cmd, flags)) { }
	impl(const impl& p) : ptr(p.ptr) { }
};

//####################################################################################################
// popen
//####################################################################################################

process::popen::popen(const char *cmd, int flags) : pimpl(*(new impl(cmd, flags)))
{ }

process::popen::popen(const std::string& cmd, int flags) : pimpl(*(new impl(cmd.c_str(), flags)))
{ }

process::popen::popen(const process::popen& p) : pimpl(*(new impl(p.pimpl)))
{ }

int process::popen::poll(void) const
{
	return pimpl.ptr->poll();
}

int process::popen::wait(void)
{
	return pimpl.ptr->wait();
}

int process::popen::pid(void) const
{
	return pimpl.ptr->pid_;
}

std::ostream& process::popen::stdin(void)
{
	return *(pimpl.ptr->to_.get());
}

std::istream& process::popen::stdout(void)
{
	return *(pimpl.ptr->from_.get());
}

std::istream& process::popen::stderr(void)
{
	return *(pimpl.ptr->error_.get());
}

void process::popen::close_stdin(void)
{
	pimpl.ptr->to_.reset(0);
}

process::popen::~popen()
{
    delete &pimpl;
}

//####################################################################################################

process::popen process::popen2(const char *cmd)
{
	return popen(cmd, popen::with_stdin | popen::with_stdout);
}

process::popen process::popen3(const char *cmd)
{
	return popen(cmd, popen::with_stdin | popen::with_stdout | popen::with_stderr);
}

process::popen process::popen4(const char *cmd)
{
	return popen(cmd, popen::with_stdin | popen::with_union_stdout_and_stderr);
}

void process::transfer(process::popen& proc, std::istream& in, std::ostream& out )
{
    boost::iostreams::filtering_istream f_in;
    f_in.push(proc.in_filter());
    f_in.push(in);

    boost::iostreams::copy(f_in, out);
}
